package com.luoyi.service;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson2.JSONPath;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.luoyi.util.ExportWordUtil;
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xwpf.usermodel.XWPFDocument;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * yapi 接口导出 word
 */
@Slf4j
public class YapiService {

    /**
     * word文档的地址
     */
    public final static String DOC_FILE = "D:\\fileTest\\api.docx";

    /**
     * 请求头表头
     */
    private final static String[] RQ_HEAD_CELL_NAME = new String[]{"参数名称","参数值","是否必须","示例","备注"};
    private final static Boolean[] RQ_HEAD_CELL_ALIGN = new Boolean[]{true,true,true,true,true};

    /**
     * 请求体表头
     */
    private final static String[] RQ_BODY_CELL_NAME = new String[]{"名称","类型","是否必须","备注"};
    private final static Boolean[] RQ_BODY_CELL_ALIGN = new Boolean[]{false,true,true,false};

    /**
     * 响应体表头
     */
    private final static String[] RESP_BODY_CELL_NAME = new String[]{"名称","类型","备注"};
    private final static Boolean[] RESP_BODY_CELL_ALIGN = new Boolean[]{false,true,false};

    /**
     * 响应体表头
     */
    private final static String[] REQ_PARAM_NAME = new String[]{"参数名称","示例","备注"};
    private final static Boolean[] REQ_PARAM_ALIGN = new Boolean[]{false,false,false};


    /**
     * 写入word需要的数据类
     */
    @Builder
    @Data
    public static class WordData{
        private long id;
        // 请求地址
        private String apiUrl;
        // 接口名字
        private String apiName;
        // 请求方式
        private String method;
        // 请求头   (参数名称	参数值	是否必须	示例	备注)
        private String[][] rqHeadArray;
        // 请求体body  (名称	类型	 是否必须	默认值	备注)
        private String[][] rqBodyArray;
        // 响应体body   (名称	类型	 是否必须	默认值	备注)
        private String[][] rspBodyArray;
        // 路径参数   (参数名称	示例	备注)
        private String[][] reqParamsArray;
    }

    /**
     * http 请求
     * @param url 地址
     * @return 响应
     */
    public static JSONObject get(String url, String cookie) {
        cookie = StrUtil.isEmpty(cookie) ? "ajs_user_id=635cf0d33f00de51b8b8f47c679c6be524192975; ajs_anonymous_id=0e1f9ec5-4823-43e2-814e-f2411662938c; _yapi_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEwNzc0LCJpYXQiOjE2OTUzNDM4NTcsImV4cCI6MTY5NTk0ODY1N30.4q68JvnDH03bKjAokSxb3SttBwW-Sz8SRu4wQWR5N78; _yapi_uid=10774"
                : cookie;
        HttpRequest request = HttpUtil.createGet(url)
                .header("Accept", "application/json, text/plain, */*")
                .header("Accept-Language", "zh-CN,zh;q=0.9")
                .header("Connection", "keep-alive")
                .header("Cookie", cookie)
                .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36");
        HttpResponse httpResponse = request.execute();
        if (!httpResponse.isOk()) {
            throw new RuntimeException("http请求失败...");
        }
        return JSONObject.parseObject(httpResponse.body());
    }

    /**
     * 详情请求
     * @param id 接口id
     * @param url 请求地址
     * @return 响应
     */
    public static JSONObject detail(String id, String cookie, String url) {
        JSONObject jsonObject = get(url + "/get?id=" + id, cookie);
        int errcode = jsonObject.getIntValue("errcode");
        if (errcode == 0) {
            return jsonObject.getJSONObject("data");
        }
        log.error("请求detail详情出错...");
        throw new RuntimeException(jsonObject.toJSONString());
    }

    /**
     * 请求一页接口数据
     * @param pageSize 一页显示
     * @param pageNum 页码
     * @param projectId 项目编号
     * @return 响应数据
     */
    public static List<Long> list(int pageSize, int pageNum, String projectId, String cookie, String url) {
        JSONObject jsonObject = get(url + "/list?page=" + pageNum
                + "&limit=" + pageSize + "&project_id=" + projectId, cookie);
        int errcode = jsonObject.getIntValue("errcode");
        if (errcode == 0) {
            JSONObject data = jsonObject.getJSONObject("data");
            JSONArray list = data.getJSONArray("list");
            return list.stream().map(x -> ((JSONObject) x).getLongValue("_id")).collect(Collectors.toList());
        }
        log.error("请求list列表出错...");
        throw new RuntimeException(jsonObject.toJSONString());
    }

    /**
     * 递归翻页方法，写入文档
     * @param pageSize 一页显示
     * @param pageNum 页码
     * @param projectId 项目编号
     */
    public static void pageTurning(int pageSize, int pageNum, String projectId, String cookie, String url) {
        List<Long> records = list(pageSize, pageNum, projectId, cookie, url);
        for (Long id : records) {
            JSONObject detail = detail(id.toString(), cookie, url);
            writeWord(detail);
        }
        if (records.size() != 0){
            pageTurning(pageSize, ++pageNum, projectId, cookie, url);
        }
    }

    /**
     * 将数据写入word
     * @param detail 接口数据详情
     */
    public static void writeWord(JSONObject detail) {
        WordData wordData = formatJson(detail);
        try {
            // 打开现有的Word文档
            // 创建文档对象
            XWPFDocument document = new XWPFDocument();
            File file = new File(DOC_FILE);
            boolean appendFlag = file.exists();
            if (!file.exists()) {
                log.info("文件不存在，需要新建");
            } else {
                FileInputStream fis = new FileInputStream(DOC_FILE);
                document = new XWPFDocument(fis);
            }

            double firstTextSize = 12,
                    textSize = 10.5,
                    twoTextSize = 11;

            if (appendFlag) {
                // 空格
                ExportWordUtil.writeLine(document,"\n", null, textSize);
            }

            // 创建段落对象
            ExportWordUtil.writeLine(document, "接口名称：", wordData.getApiName(), firstTextSize);
            ExportWordUtil.writeLine(document, "接口路径：", wordData.getApiUrl(), firstTextSize);
            ExportWordUtil.writeLine(document, "请求方式：", wordData.getMethod(), firstTextSize, "0000FF");

            ExportWordUtil.writeLine(document, "请求参数", null, firstTextSize);
            ExportWordUtil.writeLine(document, "Headers：", null, twoTextSize);
            ExportWordUtil.addTable(wordData.getRqHeadArray(), document, RQ_HEAD_CELL_NAME, RQ_HEAD_CELL_ALIGN);

            // 路径参数
            String[][] reqParamsArray = wordData.getReqParamsArray();
            if (ArrayUtil.isNotEmpty(reqParamsArray)) {
                ExportWordUtil.writeLine(document, "路径参数：", null, twoTextSize);
                ExportWordUtil.addTable(reqParamsArray, document, REQ_PARAM_NAME, REQ_PARAM_ALIGN);
            }

            // 请求体
            String[][] rqBodyArray = wordData.getRqBodyArray();
            if (ArrayUtil.isNotEmpty(rqBodyArray)) {
                ExportWordUtil.writeLine(document, "Body:", null, twoTextSize);
                ExportWordUtil.addTable(rqBodyArray, document, RQ_BODY_CELL_NAME, RQ_BODY_CELL_ALIGN);
            }

            // 返回参数
            String[][] rspBodyArray = wordData.getRspBodyArray();
            if (ArrayUtil.isNotEmpty(rspBodyArray)) {
                ExportWordUtil.writeLine(document, "返回数据", null, firstTextSize);
                ExportWordUtil.addTable(rspBodyArray, document, RESP_BODY_CELL_NAME, RESP_BODY_CELL_ALIGN);
            }

            // 保存文档
            FileOutputStream out = new FileOutputStream(DOC_FILE);
            document.write(out);
            out.close();
            document.close();
            log.info("Word文档生成成功！id={}", wordData.getId());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 格式化api详情json
     * @param detail 详情
     * @return 写入word所需数据
     */
    private static WordData formatJson(JSONObject detail) {
        // 获取接口地址
        String apiUrl = JSONPath.eval(detail, "$.query_path.path").toString();
        // id
        long id = detail.getLongValue("_id");
        // 请求头
        String[][] rqHeadArray = jsonArrayToArray(detail, "req_headers", x -> {
            JSONObject item = (JSONObject) x;
            return new String[]{
                    item.getString("name"),
                    item.getString("value"),
                    "1".equals(item.getString("required")) ? "是" : "非必填",
                    item.getString("example"),
                    item.getString("desc"),
            };
        });
        // 请求体body
        String[][] rqBodyArray = jsonArrayToArray(detail, "req_body_form", x -> {
            JSONObject item = (JSONObject) x;
            return new String[]{
                    item.getString("name"),
                    item.getString("type"),
                    // 这里没办法判断，给的值都是"1"
                    "非必填",
                    item.getString("desc"),
            };
        });

        if (ArrayUtil.isEmpty(rqBodyArray)) {
            // 不清楚什么情况，这个参数也需要做兼容
            String reqBodyOther = detail.getString("req_body_other");
            rqBodyArray = formatRespBody(reqBodyOther, false);
        }

        // 路径参数
        String[][] rqParamArray = jsonArrayToArray(detail, "req_params", x -> {
            JSONObject item = (JSONObject) x;
            return new String[]{
                    item.getString("name"),
                    item.getString("example"),
                    item.getString("desc")
            };
        });
        // 接口名字
        String apiName = detail.getString("title");
        // 请求方式
        String method = detail.getString("method");
        // 响应体body
        String resBodyStr = detail.getString("res_body");
        String[][] resBody = formatRespBody(resBodyStr, true);
        return WordData.builder()
                .id(id)
                .apiName(apiName)
                .apiUrl(apiUrl)
                .rqHeadArray(rqHeadArray)
                .rqBodyArray(rqBodyArray)
                .rspBodyArray(resBody)
                .reqParamsArray(rqParamArray)
                .method(method)
                .build();
    }

    /**
     * 将json中的array转成二维数组
     * @param detail json
     * @param arrayKey 数组key
     * @param mapper 方法
     * @return 二位数组
     */
    private static String[][] jsonArrayToArray(JSONObject detail, String arrayKey,
                                               Function<? super Object, ? extends String[]> mapper) {
        JSONArray array = detail.getJSONArray(arrayKey);
        if (array == null) {
            return null;
        }
        return array.stream().map(
                mapper
        ).toArray(size -> new String[size][5]);
    }

    /**
     * 将响应body的json字符串转为word所需格式数组
     * @param json 响应body json字符
     * @param respFlag 是：响应解析，三个字段；否：请求体解析，四个字段
     * @return word所需格式数组
     */
    private static String[][] formatRespBody(String json, boolean respFlag) {
        if (StrUtil.isEmpty(json)) {
            return null;
        }
        // 解析 JSON 数据
        JsonElement element = JsonParser.parseString(json);
        JsonObject jsonObject = element.getAsJsonObject();

        // 使用 LinkedHashMap 保持 JSON 中 key 的顺序
        List<Map<String, JsonElement>> orderedMap = new ArrayList<>();
        parseJsonObject(jsonObject, orderedMap, null);

        List<String[]> objects = new ArrayList<>();

        // 按顺序输出 key 值
        for (Map<String, JsonElement> item : orderedMap) {
            for (String key : item.keySet()) {
                JsonObject asJsonObject = item.get(key).getAsJsonObject();
                JsonElement type = asJsonObject.get("type");
                JsonElement description = asJsonObject.get("description");
                if (respFlag) {
                    objects.add(new String[]{
                            key, type == null ? "" : type.getAsString(), description == null ? "" : description.getAsString()
                    });
                } else {
                    objects.add(new String[]{
                            key, type == null ? "" : type.getAsString(), "非必填", description == null ? "" : description.getAsString()
                    });
                }
            }
        }
        return objects.toArray(new String[objects.size()][3]);
    }

    /**
     * 递归解析 JSON 对象，将 key 值放入有序的 Map 中
     * @param item json
     * @param list 存储list
     * @param parentKey 上级key
     */
    private static void parseJsonObject(JsonObject item, List<Map<String, JsonElement>> list, String parentKey) {
        JsonObject object = item.getAsJsonObject("properties");
        if (object == null) {
            JsonElement type = item.get("type");
            if (type != null && "\"array\"".equals(type.toString())) {
                // 有下面的参数
                JsonObject items = item.getAsJsonObject("items");
                object = items.getAsJsonObject("properties");
                if (object == null) {
                    return;
                }
            } else {
                return;
            }
        }

        for (Map.Entry<String, JsonElement> entry : object.entrySet()) {
            String key = entry.getKey();
            JsonElement value = entry.getValue();
            HashMap<String, JsonElement> map = new HashMap<>();

            String newKey;
            if (StrUtil.isEmpty(parentKey)) {
                newKey = key;
            } else {
                newKey = parentKey + "." + key;
            }
            map.put(newKey, value);
            list.add(map);

            // 如果 JSON 对象的值还是一个嵌套的 JSON 对象，继续递归解析
            if (value.isJsonObject()) {
                parseJsonObject(value.getAsJsonObject(), list, newKey);
            } else if (value.isJsonArray()) {
                JsonArray asJsonArray = value.getAsJsonArray();
                for (JsonElement jsonElement : asJsonArray) {
                    parseJsonObject(jsonElement.getAsJsonObject(), list, newKey);
                }
            }
        }
    }

    public static void main(String[] args) {
        // 单个id测试
        String url = "";

        JSONObject detail = detail("31352", null, url);
        JSONArray array = detail.getJSONArray("req_body_form");
        List<JSONObject> collect = array.stream().map(
                x -> {
                    JSONObject item = (JSONObject) x;
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("filedName", item.getString("name"));
                    jsonObject.put("type", item.getString("type"));
                    jsonObject.put("required", item.getString("required"));
                    jsonObject.put("desc", item.getString("desc"));
                    return jsonObject;
                }
        ).collect(Collectors.toList());
        System.out.println(JSON.toJSONString(collect));
        // 调试
        pageTurning(10, 1, "8868", null,
                url);
        log.info("完成导出 .....");
    }

}
